/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.helper;

import java.util.ArrayList;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ElementImport;
import org.eclipse.uml2.uml.Extension;
import org.eclipse.uml2.uml.Package;

import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.core.utils.VectorCalculus;
import com.cea.papyrus.diagraminterchange2.di2.Di2Factory;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * 
 */
public class GraphEdgeHelper implements IGraphEdgeHelper {

	/**
	 * 
	 */
	protected final int channel=10;

	/**
	 * 
	 */
	protected static final int SPACE_X=100;

	/**
	 * 
	 */
	public static final int SOURCE = 0;

	/**
	 * 
	 */
	public static final int TARGET = 1;


	/**
	 * This method is used to create a graphNode with a UmlSemanticModelBrige connected to a uml element.
	 * 
	 * @param umlChild 
	 * @param umlElement this is the uml elment that will be connected to the UMLSemanticBridge
	 * @param size 
	 * @param location 
	 * @param parent 
	 * 
	 * @return the graphNode that is linked to theUMLSemanticBridge.
	 */
	public GraphElement createGraphElement(Element umlChild, GraphElement parent, Point location, Dimension size){
		//create GraphNode
		GraphEdge graphEdge=Di2Factory.eINSTANCE.createGraphEdge();
		setGraphElementPreferences(graphEdge);
		graphEdge.setLineStyle("solid");
		graphEdge.setLineThickness(1);
		graphEdge.setForegroundColor(new RGB(0,0,0));//black
		graphEdge.setIsVisible(true);//createUMLbridgee
		if(umlChild!=null){
			Uml1SemanticModelBridge semantic= Di2Factory.eINSTANCE.createUml1SemanticModelBridge();
			//associate semantic with graphNode
			graphEdge.setSemanticModel(semantic);
			semantic.setElement(umlChild);}

		if(parent!=null){
			parent.getContained().add(graphEdge);
			}
		return graphEdge;
	}

	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.common.factories.IGraphEdgeFactory#createGraphEdge(org.eclipse.uml2.uml.Element, com.cea.papyrus.diagraminterchange2.di2.Diagram)
	 */

	/**
	 * 
	 * 
	 * @param umlElement 
	 * @param diagram 
	 * 
	 * @return 
	 */
	public ArrayList createGraphEdge(Element umlElement, Diagram diagram){
		return new ArrayList();
	}

	/**
	 * 
	 * 
	 * @param ge 
	 * @param umlsource 
	 * @param diagram 
	 * @param umlTarget 
	 */
	public void endCreateGraphElement(Element umlsource, Element umlTarget, GraphEdge ge, Diagram diagram){
		ArrayList di2suppliers=LookForElement.lookForGraphElementWODiagram(umlTarget, diagram, new ArrayList());
		ArrayList di2sources=LookForElement.lookForGraphElementWODiagram(umlsource, diagram, new ArrayList());
		Package topPackage= LookForElement.topPackage(((Uml1SemanticModelBridge)diagram.getOwner()).getElement());

		GraphElement source;
		GraphElement target;

		if ((di2sources.size()!=0) && (di2suppliers.size()!=0)){
			source=(GraphNode) di2sources.get(0);
			target=(GraphNode) di2suppliers.get(0);
		}

		//the source does not exists
		else if((di2sources.size()==0) && (di2suppliers.size()!=0)){
			//create the source
			Element ei= LookForElement.getElementImport(topPackage, umlsource);
			if(ei==null){
				ei=umlsource;
			}

			if (!(ei instanceof ElementImport)){
				source=ModelerHelper.getFactory().createGraphElement(ei, diagram, null, null);}

			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				NodeClassHelper nch= new NodeClassHelper();
				source=nch.createGraphElement(ei, diagram, null, null);
				//source=ModelerHelper.getFactory().createGraphElement(((ElementImport)ei).getImportedElement(), diagram, null, null);
			}
			target=(GraphNode) di2suppliers.get(0);
//			position of the target
			if(source instanceof GraphNode){
				source.setPosition(new Point(target.getPosition().x-((GraphNode)source).getSize().width-SPACE_X,target.getPosition().y));
			}

		}
		//the target does not exist
		else if((di2sources.size()!=0) && (di2suppliers.size()==0)){
			//create the supplier
			source = (GraphNode)di2sources.get(0);
			Element ei= LookForElement.getElementImport(topPackage, umlTarget);
			if(ei==null){
				ei=umlTarget;
			}

			if (!(ei instanceof ElementImport)){
				target=ModelerHelper.getFactory().createGraphElement(ei, diagram, null, null);}

			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				NodeClassHelper nch= new NodeClassHelper();
				target=nch.createGraphElement(ei, diagram, null, null);
//				target=ModelerHelper.getFactory().createGraphElement(((ElementImport)ei).getImportedElement(), diagram, null, null);
			}
//			position of the target
			if(target instanceof GraphNode){
				target.setPosition(new Point(source.getPosition().x+((GraphNode)source).getSize().width+SPACE_X,source.getPosition().y));
			}
		}
		else {
//			create all
			ArrayList GraphNodes=null;
			Element ei= LookForElement.getElementImport(topPackage, umlsource);
			if(ei==null){
				ei=umlsource;
			}
			if (!(ei instanceof ElementImport)){
				source=ModelerHelper.getFactory().createGraphElement(ei, diagram, null, null);}

			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				//source=ModelerHelper.getFactory().createGraphElement(((ElementImport)ei).getImportedElement(), diagram, null, null);
				NodeClassHelper nch= new NodeClassHelper();
				source=nch.createGraphElement(ei, diagram, null, null);
			}
			ei=null;
			if(umlTarget.getOwner()instanceof Package){
				ei= LookForElement.getElementImport(((Package)umlTarget.getOwner()), umlTarget);}
			if(ei==null){
				ei=umlTarget;
			}

			if (!(ei instanceof ElementImport)){
				target=ModelerHelper.getFactory().createGraphElement(ei, diagram, null, null);}
			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				NodeClassHelper nch= new NodeClassHelper();
				target=nch.createGraphElement(ei, diagram, null, null);
				//target=ModelerHelper.getFactory().createGraphElement(((ElementImport)ei).getImportedElement(), diagram, null, null);
			}
			//target = (GraphNode)GraphNodes.get(0);

//			position of the target
			if (target instanceof GraphNode){
				target.setPosition(new Point(source.getPosition().x+((GraphNode)source).getSize().width+SPACE_X,source.getPosition().y));
			}
		}

		//all element exist!
		//1. create connector
		GraphConnector sourceConnector=Di2Factory.eINSTANCE.createGraphConnector();
		GraphConnector targetConnector=Di2Factory.eINSTANCE.createGraphConnector();

		//2. calculate position for each element

		//2.1 position of the source connector
		Point p = new Point(((GraphNode)source).getPosition());
		if(source.getContainer() instanceof Diagram)
			p = LookForElement.calculateAbsolutePosition((GraphNode)source, new Point(0,0));
		sourceConnector.setPosition(new Point (p.x+((GraphNode)source).getSize().width, p.y+((GraphNode)source).getSize().height/2));
		//2.2 position if the target Connector
		p = new Point(((GraphNode)target).getPosition());
		if(target.getContainer() instanceof Diagram)
			p = LookForElement.calculateAbsolutePosition((GraphNode)target, new Point(0,0));
		targetConnector.setPosition(new Point(p.x, p.y+((GraphNode)target).getSize().height/2));

		//3. Connection of all element
		//3.1 connect anchor to graphEdge
		
		((GraphEdge)ge).getAnchor().add(SOURCE,sourceConnector);
		((GraphEdge)ge).getAnchor().add(TARGET,targetConnector);
		((GraphConnector)ge.getAnchor().get(SOURCE)).setGraphElement(source);
		((GraphConnector)ge.getAnchor().get(TARGET)).setGraphElement(target);
		if( ((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof Extension){
			VectorCalculus.setGoodPositionForExtension(ge,(GraphNode)source,(GraphNode)target);
		} else if( ((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof Association){
			VectorCalculus.setGoodPositionForAssociation(ge,(GraphNode)source,(GraphNode)target);}

		Debug.debug(this,"   target dependency link connected", channel);
		finalizeEndCreateGraphElement(ge, (GraphNode)source,(GraphNode)target);
	}

	/**
	 * this method is used to finalize the {@link #endCreateGraphElement(Element, Element, GraphEdge, Diagram)} and 
	 * and add the link to the parent
	 * @param edge the link that we want to finalize
	 * @param source the source of the link
	 * @param target the target of the link
	 */
	protected void finalizeEndCreateGraphElement(GraphEdge edge, GraphNode source,GraphNode target){
		
		((GraphNode)source.getContainer()).getContained().add(edge);
	}

	/**
	 * 
	 * 
	 * @param parent 
	 * 
	 * @return 
	 */
	public boolean canCreateGraphElement(GraphElement parent) {

		return false;
	}


	/**
	 * 
	 * 
	 * @param child 
	 * @param parent 
	 * 
	 * @return 
	 */
	public boolean removeGraphElement(GraphElement child, GraphElement parent){
		return parent.getContained().remove(child);
	}

	/**
	 * 
	 * 
	 * @param child 
	 * @param parent 
	 * 
	 * @return 
	 */
	public GraphElement addGraphElement(GraphElement child, GraphElement parent) {
		parent.getContained().add(child);
		return child;
	}

	///////////////////////////////////////////////////////////////////////////////////
	//                      UML Management
	///////////////////////////////////////////////////////////////////////////////////

	/**
	 * 
	 * 
	 * @return 
	 */
	public String getBaseString() {
		// TODO Auto-generated method stub
		return "";
	}

	/**
	 * 
	 * 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	public String getNewUMLElementName(Element umlParent) {
		String name = ""; //$NON-NLS-1$

		// i <10000: avoid infinite loops
		for(int i=0; i< 10001; i++ ) {
			name = getBaseString()+i;
			if(null == umlParent.getNearestPackage().getPackagedElement(name)) {
				return name;
			}
		}
		return getBaseString()+"X"; //$NON-NLS-1$

	}

	/**
	 * 
	 * 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	public boolean canCreateUMLElement(Element umlParent) {
		// TODO Auto-generated method stub
		return false;
	}

	/**
	 * 
	 * 
	 * @param umlElement 
	 * @param diagram 
	 * 
	 * @return 
	 */
	public ArrayList createGraphNode(Element umlElement, Diagram diagram) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 
	 * 
	 * @param element 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	public boolean addUMLElement(Element element, Element umlParent) {
		// TODO Auto-generated method stub
		return false;
	}

	/**
	 * 
	 * 
	 * @param umlParent 
	 * @param name 
	 * 
	 * @return 
	 */
	public Element createUMLElement(String name, Element umlParent) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 
	 * 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	public Element createUMLElement(Element umlParent) {
		return createUMLElement(getNewUMLElementName(umlParent), umlParent);
	}

	/**
	 * 
	 * 
	 * @param element 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	public boolean removeUMLElement(Element element, Element umlParent) {
		// TODO Auto-generated method stub
		return false;
	}

	//////////////////////////////////////////////////////////////////////////////////
	//                                Link Management
	///////////////////////////////////////////////////////////////////////////////////
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.factories.IGraphEdgeFactory#canConnect(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param target 
	 * @param source 
	 * 
	 * @return 
	 */
	public boolean canConnect(Element source, Element target) {
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.factories.IGraphEdgeFactory#connect(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param target 
	 * @param link 
	 * @param source 
	 * 
	 * @return 
	 */
	public boolean connect(Element source, Element target, Element link) {
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.factories.IGraphEdgeFactory#deconnect(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param target 
	 * @param link 
	 * @param source 
	 * 
	 * @return 
	 */
	public boolean deconnect(Element source, Element target, Element link) {
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.factories.IGraphElementFactory#setGraphElementPreferences(com.cea.papyrus.diagraminterchange2.di2.GraphElement)
	 */
	/**
	 * 
	 * 
	 * @param graphElement 
	 */
	public void setGraphElementPreferences(GraphElement graphElement) {
		graphElement.setBackgroundColor(new RGB(0,0,0));//black
		graphElement.setBorderColor(new RGB(0,0,0));//black
		graphElement.setFontColor(new RGB(0,0,0));//black
		graphElement.setFontFamily("Arial");
		graphElement.setFontSize(9);
	}




}
